package com.tianji.learning.service.impl;

import cn.hutool.core.bean.BeanUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import com.baomidou.mybatisplus.extension.toolkit.Db;
import com.tianji.api.client.course.CatalogueClient;
import com.tianji.api.client.course.CourseClient;
import com.tianji.api.dto.IdAndNumDTO;
import com.tianji.api.dto.course.CataSimpleInfoDTO;
import com.tianji.api.dto.course.CourseFullInfoDTO;
import com.tianji.api.dto.course.CourseSimpleInfoDTO;
import com.tianji.common.domain.dto.PageDTO;
import com.tianji.common.domain.query.PageQuery;
import com.tianji.common.exceptions.BadRequestException;
import com.tianji.common.utils.CollUtils;
import com.tianji.common.utils.DateUtils;
import com.tianji.common.utils.UserContext;
import com.tianji.learning.domain.po.LearningLesson;
import com.tianji.learning.domain.po.LearningRecord;
import com.tianji.learning.domain.vo.LearningLessonVO;
import com.tianji.learning.domain.vo.LearningPlanPageVO;
import com.tianji.learning.domain.vo.LearningPlanVO;
import com.tianji.learning.enums.LessonStatus;
import com.tianji.learning.enums.PlanStatus;
import com.tianji.learning.mapper.LearningLessonMapper;
import com.tianji.learning.service.ILearningLessonService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * <p>
 * 学生课程表 服务实现类
 * </p>
 *
 * @author taohongrun
 * @since 2025-02-01
 */
@Service
@RequiredArgsConstructor
public class LearningLessonServiceImpl extends ServiceImpl<LearningLessonMapper, LearningLesson> implements ILearningLessonService {

    private final CourseClient courseClient;
    private final CatalogueClient catalogueClient;

    @Override
    @Transactional
    public void addUserLessons(Long userId, List<Long> courseIds) {
        List<LearningLesson> learningLessons = new ArrayList<>();
        List<CourseSimpleInfoDTO> cInfoList = courseClient.getSimpleInfoList(courseIds);
        if (CollUtils.isEmpty(cInfoList)) {
            // 课程不存在，无法添加
            log.error("课程信息不存在，无法添加到课表");
            return;
        }
        for (CourseSimpleInfoDTO course : cInfoList) {
            LearningLesson learningLesson = new LearningLesson();
            learningLesson.setCourseId(course.getId());
            learningLesson.setUserId(userId);
            learningLesson.setCreateTime(LocalDateTime.now());
            learningLesson.setUpdateTime(LocalDateTime.now());
            Integer validDuration = course.getValidDuration();
            if (validDuration != null && validDuration > 0) {
                LocalDateTime now = LocalDateTime.now();
                learningLesson.setCreateTime(now);
                learningLesson.setExpireTime(now.plusMonths(validDuration));
            }
            //null则说明永久有效，无需添加过期时间，默认永久有效
            learningLessons.add(learningLesson);
        }
        saveBatch(learningLessons);
    }

    @Override
    public PageDTO<LearningLessonVO> queryMyLessons(PageQuery pageQuery) {
        List<LearningLessonVO> learningLessonVOS = new ArrayList<>();
        Long user = UserContext.getUser();
        //对获取的信息进行判断
        if (user == null) throw new BadRequestException("用户未登录");
        Page<LearningLesson> learningLessonPage = lambdaQuery()
                .eq(LearningLesson::getUserId, user)
                .page(pageQuery.toMpPage("latest_learn_time", false));
        List<LearningLesson> records = learningLessonPage.getRecords();
        //对获取的信息进行判断
        if (CollUtils.isEmpty(records)) {
            return PageDTO.empty(learningLessonPage);
        }

        List<Long> courseIds = records.stream().map(LearningLesson::getCourseId).collect(Collectors.toList());
        //对获取的信息进行判断
        List<CourseSimpleInfoDTO> simpleInfoList = courseClient.getSimpleInfoList(courseIds);
        if (CollUtils.isEmpty(simpleInfoList)) {
            throw new BadRequestException("课程信息为空");
        }

        Map<Long, CourseSimpleInfoDTO> courseSimpleInfoDTOMap = simpleInfoList.stream()
                .collect(Collectors.toMap(CourseSimpleInfoDTO::getId, c -> c));
        for (LearningLesson learningLesson : records) {
            LearningLessonVO learningLessonVO = new LearningLessonVO();
            BeanUtils.copyProperties(learningLesson, learningLessonVO);
            CourseSimpleInfoDTO courseSimpleInfoDTO = courseSimpleInfoDTOMap.get(learningLesson.getCourseId());
            learningLessonVO.setSections(courseSimpleInfoDTO.getSectionNum());
            learningLessonVO.setCourseCoverUrl(courseSimpleInfoDTO.getCoverUrl());
            learningLessonVO.setCourseName(courseSimpleInfoDTO.getName());
            learningLessonVOS.add(learningLessonVO);
        }
        return PageDTO.of(learningLessonPage, learningLessonVOS);
    }

    @Override
    public LearningLessonVO queryMyCurrentLessons() {
        Long user = UserContext.getUser();
        //对获取的信息进行判断
        if (user == null) throw new BadRequestException("用户未登录");

        //1.排序后查询一个 且为学习中
        LearningLesson learningLesson = lambdaQuery()
                .eq(LearningLesson::getUserId, user)
                .eq(LearningLesson::getStatus, LessonStatus.LEARNING)
                .orderByDesc(LearningLesson::getLatestLearnTime)
                .last("limit 1")
                .one();
        //对获取的信息进行判断
        if (learningLesson == null) return null;

        LearningLessonVO learningLessonVO = new LearningLessonVO();
        BeanUtils.copyProperties(learningLesson, learningLessonVO);

        //2.远程调用获取其他信息
        //2.1补充课程信息
        CourseFullInfoDTO courseInfoById = courseClient.getCourseInfoById(learningLessonVO.getCourseId(), false, false);
        //对获取的信息进行判断
        if (courseInfoById == null) throw new BadRequestException("课程信息错误");
        learningLessonVO.setCourseName(courseInfoById.getName());
        learningLessonVO.setCourseCoverUrl(courseInfoById.getCoverUrl());
        learningLessonVO.setSections(courseInfoById.getSectionNum());
        //2.2补充课程总数信息
        Long count = lambdaQuery().eq(LearningLesson::getUserId, user).count();
        learningLessonVO.setCourseAmount(count.intValue());
        //2.3补充小节信息
        List<CataSimpleInfoDTO> cataSimpleInfoDTOS = catalogueClient.batchQueryCatalogue(CollUtils.singletonList(learningLesson.getLatestSectionId()));
        //对获取的信息进行判断
        if (CollUtils.isEmpty(cataSimpleInfoDTOS)) throw new BadRequestException("小节信息错误");
        CataSimpleInfoDTO cataSimpleInfoDTO = cataSimpleInfoDTOS.get(0);
        learningLessonVO.setLatestSectionName(cataSimpleInfoDTO.getName());
        learningLessonVO.setLatestSectionIndex(cataSimpleInfoDTO.getCIndex());

        return learningLessonVO;
    }

    @Override
    public void createLearningPlan(Long courseId, Integer freq) {
        // 1.获取当前登录的用户
        Long userId = UserContext.getUser();
        // 2.查询课表中的指定课程有关的数据
        LearningLesson lesson = lambdaQuery().eq(LearningLesson::getUserId, userId).eq(LearningLesson::getCourseId, courseId).one();
        if (lesson == null) throw new BadRequestException("课程不存在");
        // 3.修改数据
        LearningLesson l = new LearningLesson();
        l.setId(lesson.getId());
        l.setWeekFreq(freq);
        if (lesson.getPlanStatus() == PlanStatus.NO_PLAN) {
            l.setPlanStatus(PlanStatus.PLAN_RUNNING);
        }
        updateById(l);
    }

    @Override
    public LearningPlanPageVO queryMyPlans(PageQuery query) {
        LearningPlanPageVO result = new LearningPlanPageVO();
        // 1.获取当前登录用户
        Long userId = UserContext.getUser();
        // 2.获取本周起始时间
        LocalDate now = LocalDate.now();
        LocalDateTime begin = DateUtils.getWeekBeginTime(now);
        LocalDateTime end = DateUtils.getWeekEndTime(now);
        // 3.查询总的统计数据
        // 3.1.本周总的已学习小节数量
        Long weekFinished = Db.lambdaQuery(LearningRecord.class)
                .eq(LearningRecord::getUserId, userId)
                .eq(LearningRecord::getFinished, true)
                .gt(LearningRecord::getFinishTime, begin)
                .lt(LearningRecord::getFinishTime, end)
                .count();
        result.setWeekFinished(Integer.valueOf(weekFinished.intValue()));
        // 3.2.本周总的计划学习小节数量
        QueryWrapper<LearningLesson> learningLessonQueryWrapper = new QueryWrapper<>();
        learningLessonQueryWrapper.select("sum(week_freq) as plansTotal");
        learningLessonQueryWrapper.eq("user_id", userId);
        learningLessonQueryWrapper.eq("plan_status", PlanStatus.PLAN_RUNNING);
        learningLessonQueryWrapper.in("status", LessonStatus.NOT_BEGIN, LessonStatus.LEARNING);
        Map<String, Object> map = getMap(learningLessonQueryWrapper);
        //对于查询的数据进行非null判断
        if (map == null) result.setWeekTotalPlan(0);
        else {
            BigDecimal plansTotal = (BigDecimal) map.get("plansTotal");
            int weekTotalPlan = plansTotal.intValue();
            result.setWeekTotalPlan(weekTotalPlan);
        }
        // TODO 3.3.本周学习积分

        // 4.查询分页数据
        // 4.1.分页查询课表信息以及学习计划信息
        Page<LearningLesson> p = lambdaQuery()
                .eq(LearningLesson::getUserId, userId)
                .eq(LearningLesson::getPlanStatus, PlanStatus.PLAN_RUNNING)
                .in(LearningLesson::getStatus, LessonStatus.NOT_BEGIN, LessonStatus.LEARNING)
                .page(query.toMpPage("latest_learn_time", false));
        List<LearningLesson> records = p.getRecords();
        if (CollUtils.isEmpty(records)) {
            return result.pageInfo(PageDTO.empty(p));
        }
        // 4.2.查询课表对应的课程信息
        Map<Long, CourseSimpleInfoDTO> courseSimpleInfoDTOMap = courseClient.getSimpleInfoList(records.stream().map(LearningLesson::getCourseId).collect(Collectors.toList())).stream().collect(Collectors.toMap(CourseSimpleInfoDTO::getId, c -> c));
        // 4.3.统计每一个课程本周已学习小节数量
        List<LearningRecord> learningRecordList = Db.lambdaQuery(LearningRecord.class).in(LearningRecord::getLessonId, records.stream().map(LearningLesson::getId).collect(Collectors.toList())).eq(LearningRecord::getFinished, true).gt(LearningRecord::getFinishTime, begin).lt(LearningRecord::getFinishTime, end).list();
        Map<Long, Long> lessonFinishCount = learningRecordList.stream().collect(Collectors.groupingBy(LearningRecord::getLessonId, Collectors.counting()));
        // 4.4.组装数据VO
        List<LearningPlanVO> voList = new ArrayList<>(records.size());
        for (LearningLesson r : records) {
            // 4.4.1.拷贝基础属性到vo
            LearningPlanVO vo = BeanUtil.copyProperties(r, LearningPlanVO.class);
            // 4.4.2.填充课程详细信息
            CourseSimpleInfoDTO cInfo = courseSimpleInfoDTOMap.get(r.getCourseId());
            if (cInfo != null) {
                vo.setCourseName(cInfo.getName());
                vo.setSections(cInfo.getSectionNum());
            }
            // 4.4.3.每个课程的本周已学习小节数量
            if(lessonFinishCount.get(r.getId()) != null)
            vo.setWeekLearnedSections(lessonFinishCount.get(r.getId()).intValue());
            else
                vo.setWeekLearnedSections(0);
            voList.add(vo);
        }
        return result.pageInfo(p.getTotal(), p.getPages(), voList);
    }

    @Override
    public LearningLessonVO queryLessonByCourseId(Long courseId) {
        // 1.获取当前登录用户
        Long userId = UserContext.getUser();
        // 2.查询课程信息 select * from xx where user_id = #{userId} AND course_id = #{courseId}
        LearningLesson lesson = lambdaQuery().eq(LearningLesson::getUserId,userId).eq(LearningLesson::getCourseId,courseId).one();
        if (lesson == null) {
            return null;
        }
        // 3.处理VO
        LearningLessonVO learningLessonVO = new LearningLessonVO();
         BeanUtils.copyProperties(lesson,learningLessonVO);
         return learningLessonVO;
    }
}
